﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using PacketRush.Core;
using System.Net;
using PacketRush.Services.UserManagement;
using PacketRush.Services.Messaging;
namespace ChatServer
{
    public partial class MainForm : Form
    {
        NetServer _NetServer;
        NetClient _NetClient;
        System.Timers.Timer _ServerTimer;
        System.Timers.Timer _ClientTimer;
        ServerNetUserService _ServerUserService;
        ClientNetUserService _ClientUserService;
        ClientMessagingService _ClientMessagingService;
        ServerMessagingService _ServerMessagingService;
        volatile bool _ServerRunning = false;
        volatile bool _ClientRunning = false;
        volatile bool _Updating = false;
        public MainForm()
        {
            InitializeComponent();
        }

        private void MainForm_Load(object sender, EventArgs e)
        {
            ModeDropDownList.SelectedIndex = 0;
        }

        private void StartButton_Click(object sender, EventArgs e)
        {
            StartServer();
            StartButton.Visible = false;
            StopButton.Visible = true;
        }

        private void StartServer()
        {
            if (_NetServer == null)
            {
                _NetServer = new NetServer(IPAddress.Parse(IpAddressTextBox.Text), Convert.ToInt32(PortTextBox.Text));
                _NetServer.Error += new EventHandler<NetErrorEventArgs>(NetServer_Error);
                _ServerUserService = new ServerNetUserService(_NetServer);
                _NetServer.RegisterService(_ServerUserService);
                _ServerUserService.UserAdded += new EventHandler<NetEventArgs>(ServerUserService_UserAdded);
                _ServerUserService.UserRemoved += new EventHandler<NetEventArgs>(ServerUserService_UserRemoved);
                _ServerUserService.UserChangedName += new EventHandler<NetUserNameChangeEventArgs>(ServerUserService_UserChangedName);
                _ServerMessagingService = new ServerMessagingService();
                _NetServer.RegisterService(_ServerMessagingService);
                _NetServer.Start();
                _ServerTimer = new System.Timers.Timer(1);
                _ServerTimer.Elapsed += new System.Timers.ElapsedEventHandler(ServerTimer_Elapsed);
                _ServerRunning = true;
                ModeDropDownList.Enabled = false; // hide this to stop fiddles
                _ServerTimer.Start();
            }
        }

        void NetServer_Error(object sender, NetErrorEventArgs e)
        {
            string message = string.Format("An exception was raised whilst performing an operation on client {0}\r\n{1}\r\n", e.Connection == null ? 0 : e.Connection.Id, e.SocketErrorCode.ToString());
            OutputTextBox.Invoke(new Action<string>(AddOutputMessage), message);
        }

        private void StartClient()
        {
            if (_NetClient == null)
            {
                _NetClient = new NetClient(IPAddress.Parse(IpAddressTextBox.Text), Convert.ToInt32(PortTextBox.Text));
                _NetClient.Error += new EventHandler<NetErrorEventArgs>(NetClient_Error);
                _ClientUserService = new ClientNetUserService(_NetClient, NicknameTextBox.Text);
                _NetClient.RegisterService(_ClientUserService);
                _ClientUserService.UserAdded += new EventHandler<NetEventArgs>(ClientUserService_UserAdded);
                _ClientUserService.UserRemoved += new EventHandler<NetEventArgs>(ClientUserService_UserRemoved);
                _ClientUserService.UserChangedName += new EventHandler<NetUserNameChangeEventArgs>(ClientUserService_UserChangedName);
                _ClientMessagingService = new ClientMessagingService();
                _NetClient.RegisterService(_ClientMessagingService);
                _NetClient.Connect();

                _ClientTimer = new System.Timers.Timer(1);
                _ClientTimer.Elapsed += new System.Timers.ElapsedEventHandler(ClientTimer_Elapsed);
                _ClientRunning = true;
                ModeDropDownList.Enabled = false; // hide this to stop fiddles
                _ClientTimer.Start();
            }
        }

        void NetClient_Error(object sender, NetErrorEventArgs e)
        {
            string message = string.Format("An exception was raised whilst performing an operation on client {0}\r\n{1}\r\n", e.Connection == null ? 0 : e.Connection.Id, e.SocketErrorCode.ToString());
            OutputTextBox.Invoke(new Action<string>(AddOutputMessage), message);
        }

        void ServerTimer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
        {
            if (!_Updating)
            {
                _Updating = true;
                if (_ServerRunning)
                {
                    try
                    {
                        _NetServer.Update();

                        ClientList.Invoke(new Action(ServerUpdateLatencies));
                        while (_ServerMessagingService.Messages.Count > 0)
                        {
                            TextMessage m = _ServerMessagingService.Messages.Dequeue();
                            string nick = _ServerUserService.Users[m.ConnectionId].Name;
                            string message = string.Format("{0}: {1}", nick, m.Message);
                            OutputTextBox.Invoke(new Action<string>(AddOutputMessage), message);
                        }
                    }
                    catch (Exception x)
                    {
                        OutputTextBox.Invoke(new Action<string>(AddOutputMessage), x.Message + "\r\n" + x.StackTrace);

                    }
                }
                else
                {
                    _ServerTimer.Stop();
                    _NetServer.Stop();

                    _ServerTimer = null;
                    _NetServer = null;

                    // show the mode drop down
                    MainFormToolBar.Invoke(new Action(() => ModeDropDownList.Enabled = true));

                }
                _Updating = false;

            }
        }

        void ClientTimer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
        {
            if (!_Updating)
            {
                _Updating = true;
                if (_ClientRunning)
                {
                    try
                    {
                        _NetClient.Update();

                        ClientList.Invoke(new Action(ClientUpdateLatency));

                        while (_ClientMessagingService.Messages.Count > 0)
                        {
                            TextMessage m = _ClientMessagingService.Messages.Dequeue();
                            string nick = _ClientUserService.Users[m.ConnectionId].Name;
                            string message = string.Format("{0}: {1}", nick, m.Message);

                            OutputTextBox.Invoke(new Action<string>(AddOutputMessage), message);
                        }
                    }
                    catch (Exception x)
                    {
                        OutputTextBox.Invoke(new Action<string>(AddOutputMessage), x.Message + "\r\n" + x.StackTrace);

                    }
                }
                else
                {
                    _ClientTimer.Stop();
                    ClientList.Invoke(new Action(() => ClientList.Items.Clear()));
                    _NetClient.Disconnect();

                    _ClientTimer = null;
                    _NetClient = null;

                    // show the mode drop down
                    MainFormToolBar.Invoke(new Action(() => ModeDropDownList.Enabled = true));

                }
                _Updating = false;
            }
        }

        private void StopButton_Click(object sender, EventArgs e)
        {
            _ServerRunning = false;
            StartButton.Visible = true;
            StopButton.Visible = false;
        }

        void ServerUserService_UserChangedName(object sender, NetUserNameChangeEventArgs e)
        {
            ClientList.Invoke(new Action<int, string>(UpdateUserListName), e.ConnectionId, e.NewName);
            OutputTextBox.Invoke(new Action<string>(AddOutputMessage), String.Format("{0} is now known as {1}.", e.OldName, e.NewName));
        }

        void ServerUserService_UserRemoved(object sender, NetEventArgs e)
        {
            NetUser user = _ServerUserService.Users[e.ConnectionId];
            ClientList.Invoke(new Action<int>(RemoveFromUserList), e.ConnectionId);
            OutputTextBox.Invoke(new Action<string>(AddOutputMessage), String.Format("{0} disconnected.", user.Name));
        }

        void ServerUserService_UserAdded(object sender, NetEventArgs e)
        {
            NetUser user = _ServerUserService.Users[e.ConnectionId];
            string ip = _NetServer.GetConnectionById(user.ConnectionId).RemoteEndPoint.ToString();
            ClientList.Invoke(new Action<int, string, string>(AddToUserList), e.ConnectionId, user.Name, ip);
            OutputTextBox.Invoke(new Action<string>(AddOutputMessage), String.Format("{0} connected.", user.Name));
        }

        void ClientUserService_UserChangedName(object sender, NetUserNameChangeEventArgs e)
        {
            ClientList.Invoke(new Action<int, string>(UpdateUserListName), e.ConnectionId, e.NewName);
            OutputTextBox.Invoke(new Action<string>(AddOutputMessage), String.Format("{0} is now known as {1}.", e.OldName, e.NewName));
        }

        void ClientUserService_UserRemoved(object sender, NetEventArgs e)
        {
            NetUser user = _ClientUserService.Users[e.ConnectionId];
            ClientList.Invoke(new Action<int>(RemoveFromUserList), e.ConnectionId);
            OutputTextBox.Invoke(new Action<string>(AddOutputMessage), String.Format("{0} disconnected.", user.Name));
        }

        void ClientUserService_UserAdded(object sender, NetEventArgs e)
        {
            NetUser user = _ClientUserService.Users[e.ConnectionId];
            ClientList.Invoke(new Action<int, string, string>(AddToUserList), e.ConnectionId, user.Name, "hidden");
            OutputTextBox.Invoke(new Action<string>(AddOutputMessage), String.Format("{0} connected.", user.Name));
        }

        public void AddToUserList(int id, string name, string ip)
        {
            ListViewItem item = new ListViewItem(id.ToString());
            if (NicknameTextBox.Text == name) item.Font = new Font(item.Font.FontFamily, item.Font.Size, FontStyle.Bold);
            item.SubItems.Add(name);
            item.SubItems.Add(ip);
            item.SubItems.Add("");
            ClientList.Items.Add(item);
        }

        public void UpdateUserListName(int id, string name)
        {
            ListViewItem item = ClientList.FindItemWithText(id.ToString());
            item.SubItems[1].Text = name;
        }

        public void RemoveFromUserList(int id)
        {
            ListViewItem item = ClientList.FindItemWithText(id.ToString());
            if (item != null) ClientList.Items.Remove(item);
        }

        public void AddOutputMessage(string message)
        {
            OutputTextBox.Text += "\r\n" + message;
        }

        private void ModeDropDownList_SelectedIndexChanged(object sender, EventArgs e)
        {
            if (ModeDropDownList.SelectedIndex == 0)
            {
                StartButton.Visible = true;
                ConnectButton.Visible = NicknameLabel.Visible = NicknameTextBox.Visible = false;
            }
            else
            {
                StartButton.Visible = false;
                ConnectButton.Visible = NicknameLabel.Visible = NicknameTextBox.Visible = true;
            }
        }

        private void ConnectButton_Click(object sender, EventArgs e)
        {
            StartClient();
            ConnectButton.Visible = false;
            DisconnectButton.Visible = true;
        }

        private void DisconnectButton_Click(object sender, EventArgs e)
        {
            _ClientRunning = false;
            ConnectButton.Visible = true;
            DisconnectButton.Visible = false;
        }

        private void SendButton_Click(object sender, EventArgs e)
        {
            if (ModeDropDownList.SelectedIndex == 1)
            {
                _ClientMessagingService.SendMessage(InputTextBox.Text);
                AddOutputMessage(_ClientUserService.Name + ":" + InputTextBox.Text);
                InputTextBox.Text = "";
            }
        }

        public void ServerUpdateLatencies()
        {
            for (int i = 0; i < _NetServer.ConnectionCount; i++)
            {
                INetConnection conn = _NetServer.GetConnectionByIndex(i);

                ListViewItem item = ClientList.FindItemWithText(conn.Id.ToString());
                if (item != null)
                {
                    item.SubItems[3].Text = conn.Latency.ToString();
                }
            }
        }

        public void ClientUpdateLatency()
        {
            if (_NetClient.ConnectionState > NetClientConnectionState.Connecting)
            {
                ListViewItem item = ClientList.FindItemWithText(_NetClient.Connection.Id.ToString());

                if (item != null)
                {
                    item.SubItems[3].Text = _NetClient.Connection.Latency.ToString();
                }
            }
        }
    }
}
